﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using System.Linq;
using System.Security.Principal;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using System.Web.UI;
using phpBB;

namespace MvcApplication1.Controllers
{

    [HandleError]
    [OutputCache(Location = OutputCacheLocation.None)]
    public class AccountController : Controller
    {

        // This constructor is used by the MVC framework to instantiate the controller using
        // the default forms authentication and membership providers.

        public AccountController()
            : this(null, null)
        {
        }

        // This constructor is not used by the MVC framework but is instead provided for ease
        // of unit testing this type. See the comments at the end of this file for more
        // information.

        public AccountController(IFormsAuthentication formsAuth, phpBBMembershipProvider provider)
        {
            FormsAuth = formsAuth ?? new FormsAuthenticationService();
            MembershipService = provider ?? Membership.Provider as phpBBMembershipProvider;
        }

        public IFormsAuthentication FormsAuth
        {
            get;
            private set;
        }

        public phpBBMembershipProvider MembershipService
        {
            get;
            private set;
        }

        /// <summary>
        /// CheckAuthentication: 
        /// Following cases can happen:
        /// 1) Nothing changes in the authentication status of .NET and phpbb 
        ///    => No change
        /// 2) .NET ist still authenticated but phpbb is logged out
        ///    => Log out .NET
        /// 3) .NET is not authenticated but phpbb is logged in
        ///    => Log in .NET
        /// 4) .NET gets authenticated and cookies show phpbb still logged out
        ///    => No change
        /// 5) .NET gets logged out and coockies show phpbb still loggen in
        ///    => No change
        /// </summary>

        public void CheckAuthentication()
        {
            string UserName = "";
            bool rememberMe = false;

            if (MembershipService.IsAuthenticated(out UserName, out rememberMe))
            {
                if (!MembershipService.FormsIsAuthenticated())
                {
                    FormsAuth.SignIn(UserName, rememberMe);
                    MembershipService.ReloadPage();
                }
            }
            else
            {
                if (MembershipService.FormsIsAuthenticated())
                {
                    FormsAuth.SignOut();
                    MembershipService.ReloadPage();
                }
            }
        }

        public ActionResult Logon()
        {
            return View();
        }

        private bool ValidateLogOn(string userName, string password)
        {
            if (String.IsNullOrEmpty(userName))
            {
                ModelState.AddModelError("username", "You must specify a username.");
            }
            if (String.IsNullOrEmpty(password))
            {
                ModelState.AddModelError("password", "You must specify a password.");
            }
            if (!MembershipService.ValidateUser(userName, password))
            {
                ModelState.AddModelError("_FORM", "The username or password provided is incorrect.");
            }

            return ModelState.IsValid;
        }

        [AcceptVerbs(HttpVerbs.Post)]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
            Justification = "Needs to take same parameter type as Controller.Redirect()")]
        public ActionResult Logon(string username, string password, bool rememberMe, string returnUrl)
        {
            if (!ValidateLogOn(username, password))
            {
                ViewData["rememberMe"] = rememberMe;
                return View();
            }

            FormsAuth.SignIn(username, rememberMe);
            MembershipService.LogOn(username, rememberMe );
            
            if (!String.IsNullOrEmpty(returnUrl))
            {
                return Redirect(returnUrl);
            }
            else
            {
                return RedirectToAction("Index", "Home");
            }
        }

        public ActionResult LogOff()
        {

            FormsAuth.SignOut();
            MembershipService.SignOut();

            return RedirectToAction("Index", "Home");
        }

        public ActionResult Register()
        {
            return Redirect(MembershipService.RegistrationURL);
        }

        private static string ErrorCodeToString(MembershipCreateStatus createStatus)
        {
            // See http://msdn.microsoft.com/en-us/library/system.web.security.membershipcreatestatus.aspx for
            // a full list of status codes.
            switch (createStatus)
            {
                case MembershipCreateStatus.DuplicateUserName:
                    return "Username already exists. Please enter a different user name.";

                case MembershipCreateStatus.DuplicateEmail:
                    return "A username for that e-mail address already exists. Please enter a different e-mail address.";

                case MembershipCreateStatus.InvalidPassword:
                    return "The password provided is invalid. Please enter a valid password value.";

                case MembershipCreateStatus.InvalidEmail:
                    return "The e-mail address provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidAnswer:
                    return "The password retrieval answer provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidQuestion:
                    return "The password retrieval question provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidUserName:
                    return "The user name provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.ProviderError:
                    return "The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator.";

                case MembershipCreateStatus.UserRejected:
                    return "The user creation request has been canceled. Please verify your entry and try again. If the problem persists, please contact your system administrator.";

                default:
                    return "An unknown error occurred. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
            }
        }
    }

    // The FormsAuthentication type is sealed and contains static members, so it is difficult to
    // unit test code that calls its members. The interface and helper class below demonstrate
    // how to create an abstract wrapper around such a type in order to make the AccountController
    // code unit testable.

    public interface IFormsAuthentication
    {
        void SignIn(string userName, bool createPersistentCookie);
        void SignOut();
    }

    public class FormsAuthenticationService : IFormsAuthentication
    {
        public void SignIn(string userName, bool createPersistentCookie)
        {
            FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
        }
        public void SignOut()
        {
            FormsAuthentication.SignOut();
        }
    }
}

